//
//  Texture2D.m
//  OpenGLES_03
//
//  Created by Keisuke Hata on 09/12/24.
//  Copyright 2009 Keisuke Hata. All rights reserved.
//
#import <OpenGLES/ES1/glext.h>
#import "Texture2D.h"

@implementation Texture2D

@synthesize width = _width , height = _height , name = _name;
@synthesize realwidth = _realwidth, realheight = _realheight , img = _img , pvr = _pvr;

@dynamic hasMipmap;

- (BOOL) hasMipmap
{
	return _pvrTexture.hasMipmap;
}

- (id) initWithData : (const void *)data 
		pixelWidth:(NSUInteger)width 
		pixelHeight:(NSUInteger)height {
	
	GLint saveName;
	
	if((self = [super init])) {
		
		// テクスチャ名を生成
		glGenTextures(1, &_name);
		
		// 以前bindしていたテクスチャ
		glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName);
		glBindTexture(GL_TEXTURE_2D, _name);

		// 拡大縮小のフィルター( ミップマップは、とりあえず未使用 )
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

		// RGBA8888だけサポート
		glTexImage2D(
					 GL_TEXTURE_2D, 
					 0, 
					 GL_RGBA, 
					 width, 
					 height, 
					 0, 
					 GL_RGBA, 
					 GL_UNSIGNED_BYTE, 
					 data
					 );
		// 元に戻す
		glBindTexture(GL_TEXTURE_2D, saveName);
	}
	
	return self;
}

- (id) initWithSize :(CGSize)size {

	NSUInteger realwidth  = size.width;
	NSUInteger realheight = size.height;
	NSUInteger width      = realwidth;
	NSUInteger height     = realheight;
	
	// ２の累乗になるまで計算
	int texSize = 0;
	if((realwidth != 1) && (realwidth & (realwidth - 1))) {
		texSize = 1;
		while(texSize < realwidth)
			texSize *= 2;
		width = texSize;
	}
	if((realheight != 1) && (realheight & (realheight - 1))) {
		texSize = 1;
		while(texSize < realheight)
			texSize *= 2;
		height = texSize;
	}
	
	_width      = width;
	_height     = height;
	_realwidth  = realwidth;
	_realheight = realheight;

	void *data = nil;
	int memsize = width * height * 4;
	data = malloc( memsize );
	memset( data , 0x0 , memsize );
	
	// カラーフォーマットは、RGBA8888固定
	[self initWithData:data pixelWidth:_width pixelHeight:_height];
	
	free(data);
	return self;
}

- (id) initWithCGImage : (CGImageRef)image {
	
	CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image);
	BOOL hasAlpha = 
	   ((alphaInfo == kCGImageAlphaPremultipliedLast) || 
		(alphaInfo == kCGImageAlphaPremultipliedFirst) || 
		(alphaInfo == kCGImageAlphaLast) || 
		(alphaInfo == kCGImageAlphaFirst)) ? YES:NO;
	CGSize imageSize;
	
	if( alphaInfo == kCGImageAlphaPremultipliedLast ) {
		NSLog(@"kCGImageAlphaPremultipliedLast");
	}else {
		NSLog(@"not kCGImageAlphaPremultipliedLast");
	}


	// 画像のサイズ
	imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
	NSLog(@"w:h : %f:%f" , imageSize.width,imageSize.height);
	NSUInteger realwidth  = imageSize.width;
	NSUInteger realheight = imageSize.height;
	NSUInteger width  = realwidth;
	NSUInteger height = realheight;
	
	// ２の累乗になるまで計算
	int texSize = 0;
	if((realwidth != 1) && (realwidth & (realwidth - 1))) {
		texSize = 1;
		while(texSize < realwidth)
			texSize *= 2;
		width = texSize;
	}
	if((realheight != 1) && (realheight & (realheight - 1))) {
		texSize = 1;
		while(texSize < realheight)
			texSize *= 2;
		height = texSize;
	}
	
	_width      = width;
	_height     = height;
	_realwidth  = realwidth;
	_realheight = realheight;

	// 画像の転送
	CGColorSpaceRef colorSpace;
	void			*data   = nil;
	CGContextRef	context = nil;		

	// 画像サイズにあわせてメモリ空間を作成する
	colorSpace = CGColorSpaceCreateDeviceRGB();
	int memsize = width * height * 4;
	data = malloc( memsize );
	memset( data , 0x0 , memsize );
	
	context = CGBitmapContextCreate( 
									data, 
									width, 
									height, 
									8, 
									4 * width, 
									colorSpace, 
									kCGImageAlphaPremultipliedLast | 
									kCGBitmapByteOrder32Big 
									);
	CGColorSpaceRelease(colorSpace);
	/*
	 左下を原点としてテクスチャが描画されてしまうため２の累乗で調整することによって
	 できた余白まで描画されてしまう。
	 その分をY軸方向にずらしてあげる必要がある。
	 ImageRefの場合は、Y軸をフリップしなくても良い( 詳しくは、Quartzのドキュメントを参考に )
	 uvは、左上を原点として取得するため、余白分が描画されてしまう。
	 
	 Important: Becauseyouusealower-leftoriginwhendrawingintoabitmaporPDFcontext,youmust 
	 compensate for that coordinate system when rendering the resulting content into a view. 
	 In other words, if you create an image and draw it using the CGContextDrawImage function, 
	 the image will appear upside down by default. 
	 To correct for this, you must invert the y-axis of the CTM (by multiplying it by -1) 
	 and shift the origin from the lower-left corner to the top-right corner of the view.
	 If you use a UIImage object to wrap a CGImageRef you create, you do not need to modify the CTM. 
	 The UIImage object automatically compensates for the inverted coordinate system of the CGImageRef type.
	 */
	CGContextTranslateCTM(context, 0, height-realheight);
	CGContextDrawImage(context, 
					   CGRectMake( 
								  0, 
								  0, 
								  realwidth, 
								  realheight) , 
					   image );
	
	CGImageRef imgref = CGBitmapContextCreateImage(context);
	UIImage *imgtmp = [[UIImage alloc] initWithCGImage:imgref];
	self.img = imgtmp;
	[imgtmp release];
	CGImageRelease(imgref);

	// テクスチャの転送
	[self initWithData:data pixelWidth:_width pixelHeight:_height];
	
	CGContextRelease(context);
	free(data);
	
	return self;
}

- (id) initWithContentsOfURL : (NSURL *)url {
	
	if( ![url isFileURL] ) {
		NSLog(@"無効なURL");
		[self release];
		return nil;
	}
		
	return [self initWithContentsOfFile:[url path]];
}

- (id) initWithContentsOfFile : (NSString *)path {
	
	NSAssert( [path length] > 0 && path != nil , @"ImagePath is invalid" );
	UIImage *uiImage = nil;
	
	if( ![path isAbsolutePath] ) {
		// パスの変換
		path = [[NSBundle mainBundle] pathForResource:path ofType:nil];
	}

	// 画像パスからUIImageを生成します
	uiImage = [[UIImage alloc] initWithContentsOfFile:path];
	if ( ![self initWithCGImage:[uiImage CGImage]] ) {
		[uiImage release];
		return nil;
	}

	[uiImage release];
	return self;
}

- (void) dealloc
{
	[_pvrTexture release];
	
	if(_name) {
		glDeleteTextures(1, &_name);
		_name = 0;
	}
	[super dealloc];
}

@end

@implementation Texture2D( PVRTC )

- (id)initWithContentsOfPVRTCFile:(NSString *)path
{
	if (self = [super init] ) {
		
		_pvrTexture = [[PVRTexture pvrTextureWithContentsOfFile:path] retain];
		
		if( !_pvrTexture ) {
			[self release];
			return nil;
		}
		
		_width      = _pvrTexture.width;
		_height     = _pvrTexture.height;
		_realwidth  = _width;
		_realheight = _height;
		_name		= _pvrTexture.name;
		_pvr		= YES;
	}
	return self;
}

- (id)initWithContentsOfPVRTCURL:(NSURL *)url
{
	if( ![url isFileURL] ) {
		[self release];
		return nil;
	}
	return [self initWithContentsOfPVRTCFile:[url path]];
}

@end

@implementation Texture2D( Text )

- (id)initWithColorText:(NSString *)text 
					fontColor:(UIColor *)color
					fontAlignment:(UITextAlignment)alignment 
					fontName:(NSString *)name
					fontSize:(CGFloat)size {

	NSUInteger	width,height;
	NSUInteger	realwidth,realheight;
	CGContextRef			context;
	void*					data;
	CGColorSpaceRef			colorSpace;
	UIFont *				font;
	
	if( text == nil || [text length] == 0 ) {
		NSLog(@"テキストがnil or 長さ0です");
		[self release];
		return nil;
	}
		
	// フォントの作成
	font = [UIFont fontWithName:name size:size];
	CGSize textRealSize = [text sizeWithFont:font];
	
	// テクスチャサイズの調整
	realwidth	= textRealSize.width;
	realheight	= textRealSize.height;
	width		= realwidth;
	height		= realheight;
	
	int texSize = 0;
	if((realwidth != 1) && (realwidth & (realwidth - 1))) {
		texSize = 1;
		while(texSize < realwidth)
			texSize *= 2;
		width = texSize;
	}
	if((realheight != 1) && (realheight & (realheight - 1))) {
		texSize = 1;
		while(texSize < realheight)
			texSize *= 2;
		height = texSize;
	}
	_realwidth	= realwidth;
	_realheight = realheight;
	_width		= width;
	_height		= height;
	
	colorSpace = CGColorSpaceCreateDeviceRGB();
	data = calloc(height, width * 4); // RGBAで作成
	memset(data, 0, sizeof(GLuint) * height * width);
	// アルファが合成されたPremultipliedで転送する
	context = CGBitmapContextCreate( 
									data, 
									width, 
									height, 
									8, 
									width * 4, 
									colorSpace, 
									kCGImageAlphaPremultipliedLast 
									);
	CGColorSpaceRelease(colorSpace);
	
	UIGraphicsPushContext(context);
	
	// Quartz2Dの座標系( Y軸が左下 )
	/* 
	  _____w →X
	 |(0,0)
	 |
	 |h____
	 |(0,h)=1)
	 |
	 |
	 ↓Y

	 ↑Y
	 
	  h____
	 |
	 |
	 |_____w →X 
	 (0,0)
	 */
	// テクスチャサイズ分移動 1)
	CGContextTranslateCTM(context, 0.0, height ); 
	// 1)のY位置からY軸を反転 2)
	CGContextScaleCTM(context, 1.0, -1.0); 

	// テキストを描画
	[color set];
	[text drawInRect:CGRectMake(
								0, 
								0, 
								realwidth, 
								realheight) 
			withFont:font
			lineBreakMode:UILineBreakModeWordWrap alignment:alignment];

	UIGraphicsPopContext();
	
	// テクスチャを転送する
	self = [self initWithData:data pixelWidth:_width pixelHeight:_height];
						  
	CGContextRelease(context);
	free(data);
	
	return self;
	
}

@end
